// Growth rate calculator.
// Word represents a wrapper around alphanumeric C-style strings.
// It can be constructed implicitly and introduces some checks.

#ifndef GRATE_UTIL_WORD_H
#define GRATE_UTIL_WORD_H

#include "common.h"

#include <string.h>
#include <string>

namespace growth_rate {

class Word {
 public:
  enum WordMode {
    StringWord,
    ArrayWord
  };
  // Default constructor.
  Word() : data_(0), length_(0), mode_(ArrayWord) {}
  // Implicit constructor.
  Word(const char *word_)  // NOLINT
      : data_(reinterpret_cast<uint8_t*>(const_cast<char*>(word_))),
        length_(strlen(word_)),
        mode_(StringWord) {}
  static Word createWordFromArray(uint8_t *array, size_t length) {
    return Word(array, length, ArrayWord);
  }

  const uint8_t *data() const { return data_; }
  size_t length() const { return length_; }

  uint8_t operator[](size_t index) const {
    CHECK_LT(index, length_);
    if (mode_ == StringWord) {
      CHECK('a' <= data_[index] && data_[index] <= 'z');
      return data_[index] - 'a';
    }
    return data_[index];
  }

  std::string str() const {
    std::string result;
    for (size_t i = 0; i < length_; i++)
      result += (*this)[i] + 'a';
    return result;
  }

  Word append(const Word &other) {
    CHECK_EQ(ArrayWord, mode_);
    // Don't use memcpy here, as other word may have different type.
    for (size_t i = 0; i < other.length(); i++) {
      data_[length_ + i] = other[i];
    }
    return Word(data_, length_ + other.length(), mode_);
  }
  Word append(char letter) {
    CHECK_EQ(ArrayWord, mode_);
    CHECK('a' <= letter && letter <= 'z');
    data_[length_] = letter - 'a';
    return Word(data_, length_ + 1, mode_);
  }

  Word prefix(size_t prefix_len) const {
    DCHECK_LE(prefix_len, length_);
    return Word(data_, prefix_len, mode_);
  }

  Word drop_first(size_t n) const {
    DCHECK_LE(n, length_);
    return Word(data_ + n, length_ - n, mode_);
  }

  Word suffix(size_t suffix_len) const {
    DCHECK_LE(suffix_len, length_);
    return drop_first(length_ - suffix_len);
  }

  Word substr(size_t from, size_t length) const {
    return drop_first(from).prefix(length);
  }

  // Comparison operators.
  bool operator==(const Word &other) const {
    if (length_ != other.length()) return false;
    // Don't use memcmp, as words may be of different types.
    for (size_t i = 0; i < length_; i++) {
      if ((*this)[i] != other[i]) return false;
    }
    return true;
  }
  bool operator!=(const Word &other) const {
    return !((*this) == other);
  }

  bool is_square() const {
    if (length_ % 2 != 0) return false;
    size_t mid = length_ / 2;
    return memcmp(data_, data_ + mid, mid) == 0;
  }
  bool is_cube() const {
    if (length_ % 3 != 0) return false;
    size_t third = length_ / 3;
    return (memcmp(data_, data_ + third, third) == 0) &&
           (memcmp(data_, data_ + 2 * third, third) == 0);
  }

 private:
  // Constructor is private to avoid confusing with implicit one.
  Word(uint8_t *array, size_t length, WordMode mode)
      : data_(array),
        length_(length),
        mode_(mode) {}

  uint8_t *data_;
  size_t length_;
  WordMode mode_;
};

}  // namespace growth_rate

#endif  // GRATE_UTIL_WORD_H
